package com.iwuyc.tools.commons.util.collection;

import com.iwuyc.tools.commons.structs.basic.Pair;
import com.iwuyc.tools.commons.util.string.StringUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Predicate;

/**
 * map工具类
 * @author Neil
 * @since 2021.4
 */
public abstract class MapUtil {

    /**
     * 将pair对象转换成map对象
     *
     * @param pairs 键值对对象
     * @param <K>   键类型
     * @param <V>   值类型
     * @return map对象
     */
    @SafeVarargs
    public static <K, V> Map<K, V> asHashMap(Pair<K, V>... pairs) {
        return asHashMap(ArrayUtil.asList(pairs));
    }

    /**
     * 将pair对象转换成map对象
     *
     * @param pairs 键值对对象
     * @param <K>   键类型
     * @param <V>   值类型
     * @return map对象
     */
    public static <K, V> Map<K, V> asHashMap(Collection<Pair<K, V>> pairs) {
        if (CollectionUtil.isEmpty(pairs)) {
            return new HashMap<>();
        }
        final Map<K, V> result = new HashMap<>(pairs.size());
        for (Pair<K, V> item : pairs) {
            result.put(item.getKey(), item.getVal());
        }
        return result;
    }

    /**
     * 判断map中是否包含所有的键
     * @param map   源数据
     * @param keys 需要检查的键
     * @param <K> 键类型
     * @param <V> 值类型
     * @return 是否包含所有的键
     */
    public static <K, V> boolean containerAllKeys(Map<K, V> map, Collection<K> keys) {
        // 如果 keys 为 null 或空，认为所有键都存在（没有键需要检查）
        if (CollectionUtil.isEmpty(keys)) {
            return true;
        }

        // 如果 map 为 null 或空，但 keys 不为空，则肯定不包含所有键
        if (isEmpty(map)) {
            return false;
        }

        // 检查 keys 中的每个键是否都在 map 中存在
        return map.keySet().containsAll(keys);
    }

    /**
     * 判断map中是否包含所有的键
     *
     * @param map  源数据
     * @param keys 需要检查的键
     * @param <K>  键类型
     * @param <V>  值类型
     * @return 是否包含所有的键
     */
    public static <K, V> Map<K, V> extractKeyByKeys(Map<K, V> map, Collection<K> keys) {
        if (isEmpty(map) || CollectionUtil.isEmpty(keys)) {
            return Collections.emptyMap();
        }

        // 检查 keys 中的每个键是否都在 map 中存在
        final Map<K, V> result = new HashMap<>(keys.size());
        for (K key : keys) {
            final V val = map.get(key);
            if (val != null) {
                result.put(key, val);
            }
        }
        return result;
    }

    /**
     * 判断 map 中是否包含所有以 keyPrefix 开头的键
     *
     * @param map       待检查的 map
     * @param keyPrefix 键的起始前缀
     * @param <V>       键对应的值类型
     * @return 是否包含所有以 keyPrefix 开头的键
     */
    public static <V> Map<String, V> extractKeyByPrefix(Map<String, V> map, String keyPrefix) {
        if (StringUtils.isEmpty(keyPrefix)) {
            throw new IllegalArgumentException("keyPrefix can not be empty.");
        }
        if (isEmpty(map)) {
            return Collections.emptyMap();
        }
        return extractKeyAndVal(map, item -> {
            if (item == null || item.getKey() == null) {
                return false;
            }
            return item.getKey().startsWith(keyPrefix);
        });
    }

    /**
     * 提取map中的键和值
     *
     * @param map       源数据
     * @param predicate 条件
     * @param <K>       键类型
     * @param <V>       值类型
     * @return 提取后的结果集合
     */
    public static <K, V> Map<K, V> extractKeyAndVal(Map<K, V> map, Predicate<Map.Entry<K, V>> predicate) {
        if (isEmpty(map)) {
            return Collections.emptyMap();
        }

        final Map<K, V> result = new HashMap<>(map.size());
        for (Map.Entry<K, V> item : map.entrySet()) {
            if (predicate.test(item)) {
                result.put(item.getKey(), item.getValue());
            }
        }
        return result;
    }

    /**
     * 根据map的值获取map中的key
     *
     * @param map 源数据
     * @param val 目标的value值
     * @param <K> 键类型
     * @param <V> 值类型
     * @return 符合条件的数据列表
     */
    public static <K, V> Collection<K> findKeyByVal(Map<K, V> map, Object val) {
        if (isEmpty(map)) {
            return Collections.emptyList();
        }
        final Collection<K> result = new ArrayList<>();

        map.entrySet().stream().filter((item) -> {
            // 查找的目标是null，map里的值也是null。即查找map里val为null的key值。
            if (item.getValue() == val) {
                return true;
            }
            // 其他不为null的情况使用equal方法进行比较。
            return null != item.getValue() && item.getValue().equals(val);
        }).forEach((item) -> result.add(item.getKey()));
        return result;
    }

    /**
     * 根据key的前缀进行搜索。key按string类型进行转换。
     *
     * @param source    数据源
     * @param prefixKey 前缀
     * @param <K> 键类型
     * @param <V> 值类型
     * @return key带有 prefixKey前缀的数据
     */
    public static <K, V> Map<K, V> findEntryByPrefixKey(Map<K, V> source, String prefixKey) {
        if (isEmpty(source)) {
            return Collections.emptyMap();
        }
        if (null == prefixKey) {
            return Collections.singletonMap(null, source.get(null));
        }
        final Map<K, V> result = new HashMap<>(source.size());
        source.entrySet().stream().filter((item) -> String.valueOf(item.getKey()).startsWith(prefixKey))
                .forEach((item) -> result.put(item.getKey(), item.getValue()));
        return result;
    }

    public static boolean isEmpty(Map<?, ?> map) {
        return map == null || map.isEmpty();
    }

    public static boolean isNotEmpty(Map<?, ?> map) {
        return !isEmpty(map);
    }

}
